package se.webbzon.oschi01.collision3d;

import java.util.Collection;
import java.util.Iterator;

import javax.media.j3d.Bounds;
import javax.media.j3d.Transform3D;

public abstract class CollisionNode3D {
	
	// The boundary of this collision node
	private final Bounds boundary;
	
	// The transformed boundary of this collision node
	private final Bounds transformedBoundary;
	
	// The transform of this collision node
	private final Transform3D transform;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new collision node which covers the same
	 * volume as the given collision node. **/
	public CollisionNode3D(CollisionNode3D node) {
		this((Bounds) node.boundary.clone());
	}
	
	/** Creates a new collision node which covers the same
	 * volume as the given collection of collision nodes. **/
	public CollisionNode3D(Collection<CollisionNode3D> nodes) {
		this.transform = new Transform3D();
		Iterator<CollisionNode3D> i = nodes.iterator();
		boundary = (Bounds) i.next().boundary.clone();
		while (i.hasNext())
			boundary.combine(i.next().boundary);
		transformedBoundary = (Bounds) boundary.clone();
	}
	
	/** Creates a new collision node which covers a certain volume
	 * given by the input boundary. **/
	public CollisionNode3D(Bounds boundary) {
		this.transform = new Transform3D();
		this.boundary = boundary;
		transformedBoundary = (Bounds) boundary.clone();
	}
	
	/** Creates a new collision node which covers a certain volume
	 * given by the input boundary transformed by a given Transform3D. **/
	public CollisionNode3D(Bounds boundary, Transform3D transform) {
		this.transform = transform;
		this.boundary = boundary;
		transformedBoundary = (Bounds) boundary.clone();
		setTransform(transform);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns true if the the collision mode is an instance of
	 * CollisionModel3D or an extension of that class. **/
	public abstract boolean isModel();
	
	/** Returns the number of children attached to this collision
	 * node. **/
	public abstract int children();
	
	/** Returns the (n+1):th child attached to this collision node. **/
	public abstract CollisionNode3D getChild(int n);
	
	/** Returns true if the boundary of two collision nodes intersects. **/
	public boolean intersects(CollisionNode3D node) {
		return transformedBoundary.intersect(node.transformedBoundary);
	}
	
	/** Copies the transform of the collision node to the given 
	 * Transform3D. **/
	public void getTransform(Transform3D transform) {
		transform.set(this.transform);
	}
	
	/** Copies the transform given by the user to the transform
	 * of this collision node. **/
	public void setTransform(Transform3D transform) {
		transformedBoundary.transform(boundary,transform);
		this.transform.set(transform);
	}
	
	/** Transforms this collision node permanently. **/
	public void transform(Transform3D transform) {
		boundary.transform(transform);
		transformedBoundary.transform(transform);
	}
	
	/** Merges another collision node into this collision node. **/
	public void merge(CollisionNode3D other) {
		boundary.combine(other.boundary);
		transformedBoundary.combine(other.transformedBoundary);
	}
	
	/*============================================================
	Protected Methods
	============================================================*/
	
	/** Returns the transform associated with this collision node.
	 * This method is for read-only purpose. **/
	protected Transform3D getTransform() {
		return transform;
	}
	
	/** Returns the bounds of this collision node.
	 * This method is for read-only purpose. **/
	protected Bounds getBounds() {
		return boundary;
	}
	
}
